/*
* Nettle Source File.
* Copyright (C) 2013, Nettle (http://nttl.ru/).
*
* This file is part of NettleMath.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Description:
*
* History:
* Created by Sergey Serb, l-proger@yandex.ru
*/

#ifndef NVector4_h__
#define NVector4_h__

#include "NVectorBase.h"
#include "NVector2.h"
#include "NMathTypes.h"
#include "NMathUtils.h"
#include <type_traits>

namespace NettleMath{
    template<typename T>
    struct NVector4 : NVectorBase<T, 4>{
        T x, y, z, w;

        //constructors
        NVector4(){
        }
        NVector4(const NVector4<T>& val)
            :x(val.x), y(val.y), z(val.z), w(val.w){
        }
        NVector4(const T& val)
            :x(val), y(val), z(val), w(val){
        }
        NVector4(const T& val_x, const T& val_y, const T& val_z, const T& val_w)
            :x(val_x), y(val_y), z(val_z), w(val_w){
        }

        //methods
        void set(const T& vx, const T& vy, const T& vz, const T& vw) {
            x = vx;
            y = vy;
            z = vz;
            w = vw;
        }
        void set(const T& v) {
            x = y = z = w = v;
        }
        const NVector4<bool> isEqual3(const NVector4<T>& other){
            return NVector4<bool>(x == other.x, y == other.y, z == other.z, w == other.w);
        }
        T length(){
            return std::sqrt(x*x + y*y + z*z + w*w);
        }
        T lengthSquared(){
            return x*x + y*y + z*z + w*w;
        }
        void normalize(){
            T len = length();
			assert(len != 0 && "vec4:normalize division by zero");
            x /= len;
            y /= len;
            z /= len;
            w /= len;
        }

        //operators
        bool operator == (const NVector4& v){
            return (x == v.x) && (y == v.y) && (z == v.z) && (w == v.w);
        }
        bool operator != (const NVector4& v){
            return (x != v.x) || (y != v.y) || (z != v.z) || (w != v.w);
        }
        NVector4 operator + () const{
            return *this;
        }
        NVector4 operator - () const{
            return NVector4(-x, -y, -z, -w);
        }
        NVector4 operator + (const NVector4& v) const{
            return NVector4(x + v.x, y + v.y, z + v.z, w + v.w);
        }
        NVector4 operator - (const NVector4& v) const{
            return NVector4(x - v.x, y - v.y, z - v.z, w - v.w);
        }
        NVector4 operator * (const NVector4& v) const{
            return NVector4(x * v.x, y * v.y, z * v.z, w * v.w);
        }
        NVector4 operator * (const T& s) const{
            return NVector4(x * s, y * s, z * s, w * s);
        }
        NVector4 operator / (const T& s) const{
            assert( s != 0 );
            return NVector4(x / s, y / s, z / s, w / s);
        }
        NVector4 operator / (const NVector4& v) const{
            return NVector4(x / v.x, y / v.y, z / v.z, w / v.w);
        }
        NVector4& operator += (const NVector4& v){
            x += v.x;
            y += v.y;
            z += v.z;
            w += v.w;
            return *this;
        }
        NVector4& operator -= (const NVector4& v){
            x -= v.x;
            y -= v.y;
            z -= v.z;
            w -= v.w;
            return *this;
        }
        NVector4& operator *= (const NVector4& v){
            x *= v.x;
            y *= v.y;
            z *= v.z;
            w *= v.w;
            return *this;
        }
        NVector4& operator *= (const T& s){
            x *= s;
            y *= s;
            z *= s;
            w *= s;
            return *this;
        }
        NVector4& operator /= (const T& s){
            assert( s != 0 );
            x /= s;
            y /= s;
            z /= s;
            w /= s;
            return *this;
        }
        NVector4& operator /= (const NVector4& v){
            assert((v.x != 0) && (v.y != 0) && (v.z != 0) && (v.w != 0));
            x /= v.x;
            y /= v.y;
            z /= v.z;
            w /= v.w;
            return *this;
        }
        NVector4& operator = (const NVector4& v){
            x = v.x;
            y = v.y;
            z = v.z;
            w = v.w;
            return *this;
        }
        NVector4& operator = (const T& v){
            x = v;
            y = v;
            z = v;
            w = v;
            return *this;
        }

		//swizzles
#ifdef NMATH_SWIZZLE_ENABLE
#include "NVector4Swizzle.h"
#endif
    };

    template <typename T>
    inline NVector4<T> operator * (const T& s, const NVector4<T>& v){
        return NVector4<T>(s * v.x, s * v.y, s * v.z, s * v.w);
    }

    

    typedef NVector4<float> float4;
    typedef NVector4<double> double4;
}
#endif // NVector4_h__
